package com.example.dicos.util;


import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.*;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;


public class PicUtils {
    //以下是常量,按照阿里代码开发规范,不允许代码中出现魔法值
    private static final Logger logger = LoggerFactory.getLogger(PicUtils.class);
    private static final Integer ZERO = 0;
    private static final Integer ONE_ZERO_TWO_FOUR = 1024;
    private static final Integer NINE_ZERO_ZERO = 900;
    private static final Integer THREE_TWO_SEVEN_FIVE = 3275;
    private static final Integer TWO_ZERO_FOUR_SEVEN = 2047;
    private static final Double ZERO_EIGHT_FIVE = 0.85;
    private static final Double ZERO_SIX = 0.6;
    private static final Double ZERO_FOUR_FOUR = 0.44;
    private static final Double ZERO_FOUR = 0.4;


    /**
     * 根据指定大小压缩图片
     *
     * @param imageBytes  源图片字节数组
     * @param desFileSize 指定图片大小，单位kb
     * @return 压缩质量后的图片字节数组
     */
    public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize) {
        if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) {
            return imageBytes;
        }
        long srcSize = imageBytes.length;
        double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR);
        try {
            while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy) //指定图片的大小
                        .outputQuality(accuracy) //图片的质量，值也是在0到1，越接近于1质量越好，越接近于0质量越差
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
            }
            logger.info("图片原大小={}kb | 压缩后大小={}kb",
                    srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR);
        } catch (Exception e) {
            logger.error("【图片压缩】msg=图片压缩失败!", e);
        }
        return imageBytes;
    }


    /**
     * 压缩图方式二
     *
     * @param bytes
     * @param desFileSize
     * @throws IOException
     */
    public static byte[] compressPicCycle(byte[] bytes, long desFileSize) throws IOException {
        if (bytes == null || bytes.length <= ZERO || bytes.length < desFileSize * ONE_ZERO_TWO_FOUR) {
            return bytes;
        }
        long srcFileSize = bytes.length;
        double accuracy = getAccuracy((srcFileSize / ONE_ZERO_TWO_FOUR));//原图大小
        // 计算宽高
        BufferedImage bim = ImageIO.read(new ByteArrayInputStream(bytes));
        int srcWidth = bim.getWidth() + 400;
        int srcHeight = bim.getHeight() + 200;
        int desWidth = new BigDecimal(srcWidth).multiply(new BigDecimal(accuracy)).intValue();
        int desHeight = new BigDecimal(srcHeight).multiply(new BigDecimal(accuracy)).intValue();

        ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节输出流（写入到内存）
        Thumbnails.of(new ByteArrayInputStream(bytes))
                .size(desWidth, desHeight)
                .outputQuality(accuracy)
                .toOutputStream(bos);
        return compressPicCycle(bos.toByteArray(), desFileSize);
    }


    /**
     * 压缩图片方式三
     *
     * @param fromPath1
     * @param desFileSize
     * @return
     * @throws Exception
     */
    public static byte[] imageSet(byte[] fromPath1, long desFileSize) throws Exception {
        return imageChangeSize(fromPath1, 1000, 800, desFileSize);
    }

    /**
     * @param bytes   byte类型图片
     * @param max_wi 图片修改后最大的宽
     * @param max_he 图片修改后最大的高
     * @return
     * @throws Exception
     * @Description 更改图片内容的大小 byte【】 类型
     */
    private static byte[] imageChangeSize(byte[] bytes, int max_wi, int max_he, long desFileSize) throws Exception {
        if (bytes == null || bytes.length <= ZERO || bytes.length < desFileSize * ONE_ZERO_TWO_FOUR) {
            return bytes;
        }
        ImageIO io = null;
        float rate = 1;
        /*图片的原始宽 120*/
        int oldwi;
        /*图片的原始高 120*/
        int oldhe;
        /*图片修改后的宽 0*/
        int new_wi = 0;
        /*图片修改后的高 0*/
        int new_he = 0;
        /*拿到byte图片*/
        InputStream is = new ByteArrayInputStream(bytes);
        BufferedImage bufImg = ImageIO.read(is);
        /*图片的原始宽度*/
        oldwi = bufImg.getWidth();
        /*图片的原始高度*/
        oldhe = bufImg.getHeight();
        //
        rate = (float) oldwi / (float) oldhe;
        /*如果图片的原宽大于最大宽度，并且原高小于等于最大高度。则证明图片过宽了，将图片宽度设置为最大宽度，此时需要等比例减小高度*/
        if (oldwi > max_wi && oldhe <= max_he) {
            new_wi = max_wi;
            new_he = new Float((float) new_wi / rate).intValue();
            /*如果图片的原宽和原高都大于或者都小于其所对应的最大值，则以任意一方为主(此处以最大高度为主)*/
        } else if (oldwi >= max_wi && oldhe >= max_he || oldwi <= max_wi && oldhe <= max_he) {
            new_he = max_he;
            new_wi = new Float(new_he * rate).intValue();
            /*如果图片的原宽小于于最大宽度，并且原高大于等于最大高度。则证明图片过高了，将图片宽度设置为最大高度，此时需要等比例减小宽度*/
        } else if (oldwi <= max_wi && oldhe > max_he) {
            new_he = max_he;
            new_wi = new Float(new_he * rate).intValue();
        }
        logger.info("原宽度--->{}： | 原高度：--->{}",oldwi,oldhe);
//        System.err.println("原宽度：" + oldwi + "原高度：" + oldhe + "_" + rate);
        /*开始改变大小*/
        ImageObserver ser = null;
        BufferedImage bf = new BufferedImage(new_wi, new_he, BufferedImage.TYPE_INT_RGB);
        bf.getGraphics().drawImage(bufImg, 0, 0, new_wi, new_he, null);
        logger.info("新宽度--->{}： | 新高度：--->{}",bf.getWidth(),bf.getHeight());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        //转换编码格式JPEG
        ImageIO.write(bf, "jpeg", out);
        byte[] re = out.toByteArray();
        logger.info("【图片剪切】| 图片原大小={}kb | 压缩后大小={}kb", (bytes.length / 1024), (re.length / 1024));
        return re;
    }


    // 设置时间格式
    private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    // 生成随机数对象
    private static final Random r = new Random();
    // 水映图片位置
//    private static String basePath = Thread.currentThread().getContextClassLoader().getResource("./img/搜索图标.png").getPath();


    /**
     * 对图片进行压缩并加水印
     *
     * @param uploadFile  原图片
     * @param targetAddr  目标路径 (盘符)
     * @param ImgBasePath ImgBasePath 新文件url （那个文件夹下）
     * @param basePath    水印图的地址
     * @throws IOException
     */
    public static String generate(File uploadFile, String targetAddr, String ImgBasePath, String basePath) {
        // 传过来的图片可能是重名的，因此系统随机生成不重复名字
        String realFileName = getRandomFileName();
        // 获取图片扩展名，如jpg,png等
        String extension = getFileExtension(uploadFile);
        // 目标路径可能不存在，不存在就创建
        makeDirPath(targetAddr, ImgBasePath);
        // 获取图片相对路径（目标路径+随机名+扩展名）
        String relativeAddr = targetAddr + ImgBasePath + realFileName + extension;
        logger.debug("current relativeAddr is:" + relativeAddr);
        // 新生成文件路径(根路径+targetAddr + realFileName + extension)
        File file = new File(relativeAddr);
        // 创建缩略图并加水映
        try {
            Thumbnails.of(uploadFile)//打开一张图片作为底图
                    .size(2000, 1200) // 输出图片的大小
                    .watermark(Positions.BOTTOM_RIGHT, //水印在底图上面的位置，这为右下角
                            ImageIO.read(new File(basePath)), //读入水印图片  C:/Users/lenovo/Desktop/搜索图标.png
                            0.25f) //印图片的清晰度，这为20%
                    .outputQuality(0.8f) //输出图片的压缩比,这里压缩比为80%
                    .toFile(file); //输出文件的位置和名称
        } catch (Exception e) {
            logger.error(e.toString());
            e.printStackTrace();
        }
        return relativeAddr;
    }

    /**
     * 获取输入文件流的扩展名
     *
     * @throws IOException
     */
    private static String getFileExtension(File uploadFile) {
        String fileName = uploadFile.getName();
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 生成随机文件名，当前年月日小时分钟秒+五位随机数
     *
     * @throws IOException
     */
    public static String getRandomFileName() {
        // 生成随机数对象
        Random r = new Random();
        // 获取随机五位数
        int rannum = r.nextInt(89999) + 10000;
        // 当前时间
        String nowTimeStr = sDateFormat.format(new Date());
        return nowTimeStr + rannum;
    }

    /**
     * 目标路径文件夹不存在就创建
     *
     * @param targetAddr  目标路径
     * @param ImgBasePath 新文件夹
     * @throws IOException
     */
    private static void makeDirPath(String targetAddr, String ImgBasePath) {
        // 文件全路径
        String realFileParentPath = targetAddr + ImgBasePath;
        File dirPath = new File(realFileParentPath);
        if (!dirPath.exists()) {
            dirPath.mkdirs();
        }
    }


    /**
     * 压缩图片大小
     * oldPath -- 原地址
     * newPath -- 新地址
     * scale -- 指定图片的大小，值在0到1之间，1f就是原图大小，0.5就是原图的一半大小，这里的大小是指图片的长宽。
     * quality -- 图片的质量，值也是在0到1，越接近于1质量越好，越接近于0质量越差。
     */
    public static void compressSize(String oldPath, String newPath, float scale, float quality) {
        try {
            Thumbnails.of(oldPath)
                    .scale(scale)
                    .outputQuality(quality)
                    .toFile(newPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 自动调节精度(经验数值)
     *
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < NINE_ZERO_ZERO) {
            accuracy = ZERO_EIGHT_FIVE;
        } else if (size < TWO_ZERO_FOUR_SEVEN) {
            accuracy = ZERO_SIX;
        } else if (size < THREE_TWO_SEVEN_FIVE) {
            accuracy = ZERO_FOUR_FOUR;
        } else {
            accuracy = ZERO_FOUR;
        }
        return accuracy;
    }
}
